/**
 * @file   log.h
 * @brief  log打印输出类
 * @encode UTF-8
 * @log    date        author      notes
 *         2021.09.17  Jimmy       first version
 */
#include <thread>
#include <mutex>
#include <vector>
#include <ctime>
#include <cstdarg>

#include "../inner_libs/usr/include/utils/date_time.h"
#include "log.h"


namespace log_shell
{

#define NONE            "\033[0m"
#define FG_BLACK        "\033[38;5;0m"
#define FG_RED          "\033[38;5;9m"
#define FG_GREEN        "\033[38;5;10m"
#define FG_YELLOW       "\033[38;5;11m"
#define FG_BLUE         "\033[38;5;12m"
#define FG_MEGENTA      "\033[38;5;13m"
#define FG_CYAN         "\033[38;5;14m"
#define FG_WHITE        "\033[38;5;15m"
#define FG_GRAY         "\033[38;5;249m"
#define FG_DEFAULT      "\033[39m"


LogConfig               DebugLog::mLogConfig;
std::mutex              DebugLog::mOutMutex;

/**
 * @brief  initial
 * @param  logConfig        
 * @return int 
 * @author Jimmy 
 * @date   2021.09.17
 */
int DebugLog::init(const LogConfig & logConfig)
{
    mLogConfig = logConfig;

    return 0;
}

/**
 * @brief  init
 * @author Jimmy 
 * @date   2021.09.17
 */
int DebugLog::init(const bool & logPrintf, const LogLevel & logLevel)
{
    LogConfig logConfig;
    logConfig.logPrintf = logPrintf;
    logConfig.logLevel  = logLevel;

    return init(logConfig);
}

/**
 * @brief  获取文件名
 * @param  pathName         
 * @return std::string 
 * @author Jimmy 
 * @date   2021.09.17
 */
std::string DebugLog::getBaseName(const std::string & pathName)
{
    /* 搜索 pathName 最后一次出现 / 的位置 */
    size_t pos = pathName.rfind("/");

    if (pos != std::string::npos)
    {
        return pathName.substr(pos + 1, pathName.length());
    }

    return "";
}

/**
 * @brief  获取时间
 * @param  us               
 * @return std::string 
 * @author Jimmy 
 * @date   2021.09.17
 */
std::string DebugLog::getHumanityTimeStr(const uint64_t & us)
{
    time_t   rtcSeconds = us / 1000000;
    uint32_t precision  = us % 1000000;

    struct tm timeInfo;
    char   timeStr[32];
    /**
     * size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
     * 根据 format 中定义的格式化规则，格式化结构 timeptr 表示的时间，并把它存储在 str 中
     * reference: https://www.twle.cn/l/yufei/cplusplus/cplusplus-basic-ctime-strftime.html
     */
    // std::strftime(timeStr, sizeof(timeStr), "%F %X", localtime_r(&rtcSeconds, &timeInfo));
    std::strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", localtime_r(&rtcSeconds, &timeInfo));

    char retStr[100];
    std::snprintf(retStr, sizeof(retStr), "%s.%06u", timeStr, precision);

    return retStr;
}

/**
 * @brief  日志输出
 * @param  file             
 * @param  func             
 * @param  line             
 * @param  logLevel         
 * @param  fmt              
 * @param  ...              
 * @author Jimmy 
 * @date   2021.09.17
 */
void DebugLog::log(const std::string & file,
                   const std::string & func,
                   const uint32_t    & line,
                   const LogLevel    & logLevel,
                   const char        * fmt, ...
                  )
{
    std::unique_lock<std::mutex> lock(mOutMutex);
    va_list  ap;
    va_start(ap, fmt);

    va_list apCopy;
    va_copy(apCopy, ap);
    const int logLen = vsnprintf(nullptr, 0, fmt, apCopy);
    va_end(apCopy);

    std::string str;
    str.resize(logLen + 1);
    vsnprintf(&str[0], str.size(), fmt, ap);
    va_end(ap);

    std::string logLevelStr;
    std::string fgColor;

    switch (logLevel)
    {
        case LOG_LEVEL_FATAL:
        {
            logLevelStr = "FATAL";
            fgColor     = FG_MEGENTA;

            break;
        }
        case LOG_LEVEL_ERROR:
        {
            logLevelStr = "ERROR";
            fgColor     = FG_RED;
            
            break;
        }
        case LOG_LEVEL_WARN:
        {
            logLevelStr = "WARN";
            fgColor     = FG_YELLOW;
            
            break;
        }
        case LOG_LEVEL_INFO:
        {
            logLevelStr = "INFO";
            fgColor     = FG_GREEN;
            
            break;
        }
        case LOG_LEVEL_DEBUG:
        {
            logLevelStr = "DEBUG";
            fgColor     = FG_WHITE;
            
            break;
        }
        default:
        {
            logLevelStr = "TRACE";
            fgColor     = FG_CYAN;
            
            break;
        }   
    }

    std::string userStr(str.data(), str.size());
    std::string logStr;
    logStr = "["  + getHumanityTimeStr(nowRtc())
           + "|"  + getBaseName(file)
           + "|"  + std::to_string(line)
           + "|"  + logLevelStr
           + "]:" + userStr.c_str(); 

    if (mLogConfig.logPrintf == false)
    {
        lock.unlock();
        return;
    }

    std::cout << fgColor << logStr << FG_DEFAULT;
}

}





/* End of file */
